package com.gitee.dupe.authority.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gitee.dolt.boot.decrypt.PasswordEncoder;
import com.gitee.dolt.boot.service.SuperCacheServiceImpl;
import com.gitee.dolt.core.exception.BizException;
import com.gitee.dolt.core.exception.ValidParamException;
import com.gitee.dolt.core.exception.code.ExceptionCode;
import com.gitee.dolt.core.util.BizAssert;
import com.gitee.dolt.security.permission.AuthorizingRealm;
import com.gitee.dolt.security.properties.SecurityProperties;
import com.gitee.dolt.utils.StrUtils;
import com.gitee.dupe.authority.dao.UserMapper;
import com.gitee.dupe.authority.dto.UserRoleDTO;
import com.gitee.dupe.authority.dto.UserSaveDTO;
import com.gitee.dupe.authority.dto.UserUpdateDTO;
import com.gitee.dupe.authority.entity.Resource;
import com.gitee.dupe.authority.entity.User;
import com.gitee.dupe.authority.entity.UserRole;
import com.gitee.dupe.authority.enumeration.UserStateEnum;
import com.gitee.dupe.authority.extend.AuthorityCustomOperate;
import com.gitee.dupe.authority.service.*;
import com.gitee.dupe.common.cache.CacheKeyDefinition;
import com.gitee.dupe.common.dto.UserGeneralDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author LYH
 * @since 2020-06-30
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends SuperCacheServiceImpl<UserMapper, User> implements IUserService {

    private final IRoleService roleService;
    private final IUserRoleService userRoleService;
    private final AuthorityCustomOperate authorityCustomOperate;
    private final PasswordEncoder passwordEncoder;
    private final IResourceService resourceService;
    private final SecurityProperties securityProperties;
    private final IOrgService orgService;

    @Override
    protected String prefix() {
        return CacheKeyDefinition.USER;
    }

    @Override
    public User getOneByAccount(String account) {
        return getOne(Wrappers.<User>lambdaQuery().eq(User::getAccount, account));
    }

    @Override
    public User getOneByMobile(String mobile) {
        if (!authorityCustomOperate.userMobileUnique()) {
            throw new BizException(ExceptionCode.OPERATION_EX.build("手机号被限定不唯一"));
        }
        List<User> users = findAllByMobile(mobile);
        return users.isEmpty() ? null : users.get(0);
    }

    @Override
    public User getOneByEmail(String email) {
        if (!authorityCustomOperate.userEmailUnique()) {
            throw new BizException(ExceptionCode.OPERATION_EX.build("邮箱被限定不唯一"));
        }
        List<User> users = findAllByEmail(email);
        return users.isEmpty() ? null : users.get(0);
    }

    @Override
    public List<User> findAllByMobile(String mobile) {
        return list(Wrappers.<User>lambdaQuery().eq(User::getMobile, mobile));
    }

    @Override
    public List<User> findAllByEmail(String email) {
        return list(Wrappers.<User>lambdaQuery().eq(User::getEmail, email));
    }

    @Override
    public User getDetail(Long id) {
        User user = getByIdCache(id);
        // 注入角色信息
        List<UserRoleDTO> roles = roleService.findAllEnabledByUserId(id);
        user.setRoles(roles);
        return user;
    }

    @Override
    public User handlerSave(UserSaveDTO userSaveDTO) {
        if (userSaveDTO.getState() == null) {
            userSaveDTO.setState(UserStateEnum.NORMAL);
        }
        User user = BeanUtil.toBean(userSaveDTO, User.class);
        check(user, false);
        String password = userSaveDTO.getPassword();
        if (StrUtil.isNotEmpty(password)) {
            user.setPasswordHash(passwordEncoder.encode(password));
        }
        authorityCustomOperate.userPreSave(user);
        save(user);
        return user;
    }

    @Override
    public User handlerUpdate(UserUpdateDTO userUpdateDTO) {
        User user = BeanUtil.toBean(userUpdateDTO, User.class);
        check(user, true);
        authorityCustomOperate.userPreUpdate(user);
        Long orgId = Optional.ofNullable(user.getOrgId()).map(e -> e.getKey()).orElse(null);
        if (orgId != null) {
            BizAssert.notNull(orgService.getByIdCache(orgId), "组织架构不存在");
        }
        updateById(user);
        return user;
    }

    @Override
    public void grantRoles(Long id, Set<Long> roleIds) {
        if (roleIds == null) {
            return;
        }
        List<UserRole> userRolesDb = userRoleService.findAllByUserId(id);
        List<Long> removeRoleIds = new ArrayList<>();
        userRolesDb.forEach(e -> {
            Long roleId = e.getRoleId();
            if (!roleIds.contains(roleId)) {
                removeRoleIds.add(roleId);
            }
            Iterator<Long> roleIterator = roleIds.iterator();
            while (roleIterator.hasNext()) {
                if (roleIterator.next().equals(roleId)) {
                    roleIterator.remove();
                }
            }
        });
        userRoleService.removeByUserIdAndRoleIds(id, removeRoleIds);
        List<UserRole> userRoles = roleIds.stream().map(e -> new UserRole().setUserId(id).setRoleId(e)).collect(Collectors.toList());
        userRoleService.saveBatch(userRoles);
    }

    @Override
    public User resetPassword(Long id, String password) {
        User user = getById(id);
        user.setPasswordHash(passwordEncoder.encode(password));
        updateById(user);
        return user;
    }

    @Override
    public User changeState(Long id, UserStateEnum state) {
        User user = getById(id);
        user.setState(state);
        updateById(user);
        return user;
    }

    @Override
    public List<UserGeneralDTO> findAllByResourceIdents(List<String> idents) {
        if (CollUtil.isEmpty(idents)) {
            return Collections.emptyList();
        }
        String[] identArr = ArrayUtil.toArray(idents, String.class);
        List<Resource> allResources = resourceService.listByCache();
        List<Long> resourceIds = allResources.stream().filter(e -> {
            if (StrUtil.isEmpty(e.getIdent())) {
                return false;
            }
            List<String> split = StrUtil.split(e.getIdent(), StrUtils.C_SEMICOLON);
            return AuthorizingRealm.hasAnyPermission(split, identArr, securityProperties.getPermission().getCaseSensitive());
        }).map(Resource::getId).collect(Collectors.toList());
        if (resourceIds.isEmpty()) {
            return Collections.emptyList();
        }
        return baseMapper.findAllByResourceIdIn(resourceIds);
    }

    @Override
    public Map<Serializable, UserGeneralDTO> findUserRealNameItem(Set<Serializable> ids) {
        List<User> users = findByIds(ids, ids2 -> listByIds(ids2));
        return users.stream().collect(Collectors.toMap(User::getId, e -> new UserGeneralDTO()
                .setId(e.getId()).setRealName(e.getRealName())));
    }

    private void check(User user, boolean isUpdate) {
        checkAccount(user, isUpdate);
        checkEmail(user, isUpdate);
        checkMobile(user, isUpdate);
    }

    private void checkAccount(User user, boolean isUpdate) {
        String account = user.getAccount();
        if (StrUtil.isEmpty(account)) {
            return;
        }
        LambdaQueryWrapper<User> qw = Wrappers.<User>lambdaQuery().eq(User::getAccount, account);
        if (isUpdate) {
            qw.ne(User::getId, user.getId());
        }
        if (count(qw) > 0) {
            throw new ValidParamException("account", account, "用户名已存在");
        }
    }

    private void checkEmail(User user, boolean isUpdate) {
        String email = user.getEmail();
        if (!authorityCustomOperate.userEmailUnique() || StrUtil.isEmpty(email)) {
            return;
        }
        LambdaQueryWrapper<User> qw = Wrappers.<User>lambdaQuery().eq(User::getEmail, email);
        if (isUpdate) {
            qw.ne(User::getId, user.getId());
        }
        if (count(qw) > 0) {
            throw new ValidParamException("email", email, "邮箱已存在");
        }
    }

    private void checkMobile(User user, boolean isUpdate) {
        String mobile = user.getMobile();
        if (!authorityCustomOperate.userMobileUnique() || StrUtil.isEmpty(mobile)) {
            return;
        }
        LambdaQueryWrapper<User> qw = Wrappers.<User>lambdaQuery().eq(User::getMobile, mobile);
        if (isUpdate) {
            qw.ne(User::getId, user.getId());
        }
        if (count(qw) > 0) {
            throw new ValidParamException("mobile", mobile, "手机号已存在");
        }
    }


}
